package cn.pugle.base.thread.ticketlock;


/**
 * 问题: 当我们有一块代码, 有个某对象ID作为参数, 想要带着同一个ID的线程互斥, 带着不同ID的线程可同时进入. <br/>
 * <p>
 * 简单的想法就是synchronized(这个ID), 当然, 你很难简单让多个线程获得同一个对象锁.
 * <p>
 * Stack Overflow上对于类似情景有很多讨论:
 * <p>
 * https://stackoverflow.com/questions/17069569/thread-synchronization-based-upon-an-id <br/>
 * https://stackoverflow.com/a/27806218/5142886 <br/>
 * https://stackoverflow.com/a/660002/5142886 <br/>
 * https://stackoverflow.com/a/660063/5142886 <br/>
 * <p>
 * 思路1: 用一个ConcurrentMap, ID作为key, value是锁, 线程putIfAbsent获得锁再synchronized锁. 问题是不好处理释放对象.  <br/>
 * 这里无论是手动remove, 或是利用WeakRef自动回收, 都会遇到线程1走了, 线程2进去了, map中的东西被移除了, 线程3又能进了, 和2冲突. <br/>
 * 解决方法可能是对进入临界区的线程计数. <br/>
 * 后来感觉WeakRef应该也行
 * 思路2: wait/notify. 或者condition, signal
 * <p>
 * 2,4可用
 *
 * @author tzp
 * @since 2018/7/6
 */
public class TicketLockTest {
    public static void main(String[] args) throws InterruptedException {
        TicketLock2 ticketLock = new TicketLock2();

        MyTask myTask1 = new MyTask(1L, ticketLock);
        new Thread(myTask1).start();

        MyTask myTask2 = new MyTask(1L, ticketLock);
        Thread thread2 = new Thread(myTask2);
        thread2.start();

        thread2.join();
        System.out.println(ticketLock);
        System.out.println(ticketLock.getLock(1L));
        System.out.println(ticketLock);
    }

    static class MyTask implements Runnable {

        Long idToLock;
        TicketLock ticketLock;

        public MyTask(Long idToLock, TicketLock ticketLock) {
            this.idToLock = idToLock;
            this.ticketLock = ticketLock;
        }

        @Override
        public void run() {
            Object lock = ticketLock.getLock(idToLock);
            try {
                synchronized (lock) {
                    System.out.println("i get lock " + idToLock + ": " + lock);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("i quit lock area");
                }
            } finally {
                ticketLock.removeLock(idToLock);
            }
        }
    }

}
